﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Tecnosop.Dominio;
using Tecnosop.WebUI.Models;
using Tecnosop.WebUI.Infrastructure.Notification;
using Rotativa;

namespace Tecnosop.WebUI.Controllers
{
    public partial class FacturaConvenioController : Controller
    {
        private Entidades db = new Entidades();

        #region CRUD

        [Authorize]
        public virtual ActionResult Index()
        {
            return View();
        }

        public virtual ActionResult printPdfFacturaConvenio(int idF, string RazonSocialF)
        {
            //return View();
            return new ActionAsPdf("VistaBlancaFacturaConvenio", new { id = idF, RazonSocial = RazonSocialF }) { FileName = "Factura-convenio.pdf" };
        }

        public virtual ActionResult VistaBlancaFacturaConvenio(int id, string RazonSocial)
        {
            FACTURA_CONVENIO factura = db.FACTURA_CONVENIO.Find(id, RazonSocial);
            List<DetalleFacturaConvenioViewModel> detalles = new List<DetalleFacturaConvenioViewModel>();

            foreach (var detalle in factura.DETALLE_FACTURA_CONVENIO)
            {
                if (detalle.DETALLE_CARGO_FIJP != null)
                {
                    AgregarLineaDetalleCargoFijo(detalles, detalle.DETALLE_CARGO_FIJP);
                }
                else if (detalle.DETALLE_MEDICION != null)
                {
                    AgregarLineaDetalleMedicion(detalles, detalle.DETALLE_MEDICION, factura.NUMERO_CONVENIO);
                }
            }

            if (!(factura.FACTURA.FACTURA_EMITIDA ?? false))
            {
                factura.FACTURA.FACTURA_EMITIDA = true;
                db.Entry(factura).State = System.Data.EntityState.Modified;
                db.SaveChanges();
            }

            return View(new FacturaConvenioViewModel
            {
                NUMERO_FACTURA = factura.NUMERO_FACTURA,
                RAZON_SOCIAL = factura.RAZON_SOCIAL,
                FACTURA = factura.FACTURA,
                NUMERO_CONVENIO = factura.NUMERO_CONVENIO,
                CONVENIO = factura.CONVENIO,
                TOTAL_FACTURA = factura.FACTURA.TOTAL ?? 0,
                DetalleFactura = detalles
            });
        }


        #region Details

        private void AgregarLineaDetalleCargoFijo(IList<DetalleFacturaConvenioViewModel> detalles, DETALLE_CARGO_FIJP cargoFijo)
        {
            /* Rescato el valor de la UF segun la fecha de la factura */
            //FACTURA factura = db.FACTURA.Find(cargoFijo.NUMERO_FACTURA, cargoFijo.RAZON_SOCIAL);
            //int valorUF = db.PARAMETROS.Single(p => p.ULTIMA_MODIFICACION_UF.CompareTo(factura.FECHA_INSTALACION.Value) == 0).VALOR_UF;

            ///* Primero determino el precio del cargo fijo; antes ver si esta en UF */
            //EQUIPO_EN_CONVENIO equipo = db.EQUIPO_EN_CONVENIO.Find(cargoFijo.NUMERO_CONVENIO, cargoFijo.NUMERO_EQUIPO_EN_CONVENIO);
            //PAGO_TIPO_CARGO_FIJO cF = equipo.FORMA__DE_PAGO.First(f => f.PAGO_TIPO_CARGO_FIJO != null).PAGO_TIPO_CARGO_FIJO;
            //int precioCargoFijo = 0;

            //precioCargoFijo = cF.FORMA__DE_PAGO.UF ? (int)(cargoFijo.SUBTOTAL_.Value * valorUF) : cargoFijo.SUBTOTAL_.Value;


            detalles.Add(new DetalleFacturaConvenioViewModel
                {
                    NUMERO_LINEA_DETALLE = cargoFijo.DETALLE_FACTURA_CONVENIO,
                    DESCRIPCION_LINEA = "Cargo fijo al equipo consignación número: " + cargoFijo.EQUIPO_EN_CONVENIO.CONSIGNACION_EQUIPO.CONSIGNACION,
                    CANTIDAD = 1,
                    PRECIO = cargoFijo.SUBTOTAL_.Value,
                    //PRECIO = precioCargoFijo,
                    SUBTOTAL = cargoFijo.SUBTOTAL_.Value,
                    //SUBTOTAL = precioCargoFijo,
                    Numero_equipo_en_convenio = cargoFijo.EQUIPO_EN_CONVENIO.NUMERO_EQUIPO_EN_CONVENIO,
                    DescripcionLinea = null
                });
        }

        private void AgregarLineaDetalleMedicion(IList<DetalleFacturaConvenioViewModel> detalles, DETALLE_MEDICION detMedicion, int Num_Convenio)
        {
            EQUIPO_EN_CONVENIO equipo = db.EQUIPO_EN_CONVENIO.Find(Num_Convenio, detMedicion.MEDICIONES.NUMERO_EQUIPO_EN_CONVENIO);
            MEDICIONES medicion = db.MEDICIONES.Find(detMedicion.ID_MEDICION);//equipo.MEDICIONES.First(m => m.ID_MEDICION == detalle.DETALLE_MEDICION.ID_MEDICION);
            decimal precio_por_hoja = 0;
            bool enUF = false;
            int minimoG;

            /* Primero determino el precio por hoja de cada equipo en convenio */
            try
            {
                if (medicion.MEDICION__BN)
                {
                    MING_B_N minBN = equipo.FORMA__DE_PAGO.First(f => f.MING_B_N != null).MING_B_N;
                    enUF = minBN.FORMA__DE_PAGO.UF;
                    precio_por_hoja = enUF
                                      //? (int)(db.PARAMETROS.Single(p => p.ULTIMA_MODIFICACION_UF.CompareTo(DateTime.Today) == 0).VALOR_UF * minBN.PRECIO_HOJA.Value)
                                      ? (db.PARAMETROS.Single(p => p.ULTIMA_MODIFICACION_UF.CompareTo(DateTime.Today) == 0).VALOR_UF * minBN.FORMA__DE_PAGO.FACTOR_UF).Value
                                      : minBN.PRECIO_HOJA.Value;
                    minimoG = (int)minBN.MING;
                }
                else
                {
                    MING_COLOR minColor = equipo.FORMA__DE_PAGO.First(f => f.MING_COLOR != null).MING_COLOR;
                    enUF = minColor.FORMA__DE_PAGO.UF;
                    precio_por_hoja = enUF
                                      //? (int)(db.PARAMETROS.Single(p => p.ULTIMA_MODIFICACION_UF.CompareTo(DateTime.Today) == 0).VALOR_UF * minColor.PRECIO_HOJA.Value)
                                      ? (int)(db.PARAMETROS.Single(p => p.ULTIMA_MODIFICACION_UF.CompareTo(DateTime.Today) == 0).VALOR_UF * minColor.FORMA__DE_PAGO.FACTOR_UF)
                                      : minColor.PRECIO_HOJA.Value;
                    minimoG = (int)minColor.MING;
                }
            }
            catch /* Si ocurre un error, agrego un detalle ficticio para mostrar el error en la vista */
            {
                detalles.Add(new DetalleFacturaConvenioViewModel
                {
                    NUMERO_LINEA_DETALLE = -1,
                    DESCRIPCION_LINEA = "Detalle facturación medición - Con errores",
                    Numero_equipo_en_convenio = equipo.NUMERO_EQUIPO_EN_CONVENIO
                });

                return;
            }
            
            /* Llevo los datos pedidos para mostrar en un detalle */
            DetalleLineaMedicionViewModel DescripcionDetalle = new DetalleLineaMedicionViewModel
            {
                //Numero_serie_equipo_en_convenio = equipo.NUMERO_SERIE,
                Consignacion_equipo = equipo.CONSIGNACION_EQUIPO.CONSIGNACION,
                Direccion_equipo_en_convenio = equipo.DIRECCION_EQUIPO,
                Contador_anterior = medicion.CONTADOR_ANTERIOR_.HasValue
                                    ? medicion.CONTADOR_ANTERIOR_.Value
                                    : medicion.MEDICION__BN
                                        ? equipo.CONTADOR_SALIDA_BODEGA_EQUIPO_BN.Value
                                        : equipo.CONTADOR_SALIDA_BODEGA_EQUIPO_COLOR.Value,
                Fecha_medicion_anterior = medicion.FECHA_MEDICION_ANTERIOR,
                Contador_actual = medicion.CONTADOR_ACTUAL_,
                Fecha_medicon_actual = medicion.FECHA_MEDICION_,
                Medicion_BN = medicion.MEDICION__BN,
                EnUF = enUF,
                minimoGarantizado = minimoG
            };

            /* Detalle creado */
            detalles.Add(new DetalleFacturaConvenioViewModel
            {
                Numero_equipo_en_convenio = null,
                DESCRIPCION_LINEA = "Detalle facturación medición número: " + detMedicion.ID_MEDICION,
                NUMERO_LINEA_DETALLE = detMedicion.DETALLE_FACTURA_CONVENIO,//detalle.DETALLE_FACTURA_CONVENIO1,
                CANTIDAD = detMedicion.DIFERENCIA_FACTURADA,//DescripcionDetalle.Contador_actual - DescripcionDetalle.Contador_anterior,
                PRECIO = precio_por_hoja,
                SUBTOTAL = detMedicion.DIFERENCIA_FACTURADA * precio_por_hoja,
                DescripcionLinea = DescripcionDetalle
            });

            // Si la medicion es menor al minimo garantizado, agrego otro detalle
            if (medicion.DIFERENCIA_ < minimoG)
            {
                detalles.Add(new DetalleFacturaConvenioViewModel
                    {
                        Numero_equipo_en_convenio = null,
                        NUMERO_LINEA_DETALLE = detMedicion.DETALLE_FACTURA_CONVENIO,
                        DESCRIPCION_LINEA = "Diferencia medición " + detMedicion.ID_MEDICION + " con mínimo garantizado de " + minimoG,
                        CANTIDAD = minimoG - medicion.DIFERENCIA_,
                        PRECIO = precio_por_hoja,
                        SUBTOTAL = (minimoG - medicion.DIFERENCIA_.Value) * precio_por_hoja,
                    });
            }

            //DetalleFacturaConvenioViewModel detalleVM = new DetalleFacturaConvenioViewModel
            //{
            //    Numero_equipo_en_convenio = null,
            //    NUMERO_LINEA_DETALLE = detMedicion.DETALLE_FACTURA_CONVENIO,//detalle.DETALLE_FACTURA_CONVENIO1,
            //    CANTIDAD = DescripcionDetalle.Contador_actual - DescripcionDetalle.Contador_anterior,
            //    PRECIO = precio_por_hoja,
            //    SUBTOTAL = (DescripcionDetalle.Contador_actual - DescripcionDetalle.Contador_anterior) * precio_por_hoja,
            //    DescripcionLinea = DescripcionDetalle
            //};
        }

        [Authorize]
        public virtual ActionResult Details(int id, string RazonSocial)
        {
            FACTURA_CONVENIO factura = db.FACTURA_CONVENIO.Find(id, RazonSocial);
            List<DetalleFacturaConvenioViewModel> detalles = new List<DetalleFacturaConvenioViewModel>();

            foreach (var detalle in factura.DETALLE_FACTURA_CONVENIO)
            {
                if (detalle.DETALLE_CARGO_FIJP != null)
                {
                    AgregarLineaDetalleCargoFijo(detalles, detalle.DETALLE_CARGO_FIJP);
                }
                else if (detalle.DETALLE_MEDICION != null)
                {
                    AgregarLineaDetalleMedicion(detalles, detalle.DETALLE_MEDICION, factura.NUMERO_CONVENIO);
                }
            }

            if (factura.FACTURA.FACTURA_EMITIDA ?? false)
            {
                return View("DetalleFacturaEmitida", new FacturaConvenioViewModel
                    {
                        NUMERO_FACTURA = factura.NUMERO_FACTURA,
                        RAZON_SOCIAL = factura.RAZON_SOCIAL,
                        FACTURA = factura.FACTURA,
                        NUMERO_CONVENIO = factura.NUMERO_CONVENIO,
                        CONVENIO = factura.CONVENIO,
                        TOTAL_FACTURA = factura.FACTURA.TOTAL ?? 0,
                        DetalleFactura = detalles
                    });
            }
            else
            {
                return View(new FacturaConvenioViewModel
                    {
                        NUMERO_FACTURA = factura.NUMERO_FACTURA,
                        RAZON_SOCIAL = factura.RAZON_SOCIAL,
                        FACTURA = factura.FACTURA,
                        NUMERO_CONVENIO = factura.NUMERO_CONVENIO,
                        CONVENIO = factura.CONVENIO,
                        TOTAL_FACTURA = factura.FACTURA.TOTAL ?? 0,
                        DetalleFactura = detalles
                    });
            }
        }

        #endregion

        [Authorize]
        public virtual ActionResult Create(int id, bool razon, string RutCliente)
        {
            string RazonSocial = razon ? "Comercializadora Tecnosop" : "Juan Carlos Maldonado Huerta";
            FACTURA factura =  db.FACTURA.Find(id, RazonSocial);
            CLIENTE cliente = db.CLIENTE.Find(RutCliente);

            if (factura == null || cliente == null)
            {
                return RedirectToAction(MVC.Facturas.Create(error: true));
            }
            else
            {
                if (db.FACTURA_CONVENIO.Find(id, RazonSocial) != null)
                {
                    return RedirectToAction(Actions.Details(id, RazonSocial));
                }
                else
                {
                    return View(new CreateFacturaConvenioViewModel
                        {
                            Factura = factura,
                            Cliente = cliente,
                            Convenios = db.CONVENIO.Where(c => c.CLIENTE.RUT_CLIENTE.Equals(RutCliente) && (c.CONVENIO_VIGENTE ?? false)).ToList()
                        });
                }
            }
        }

        [HttpPost]
        [Authorize]
        public virtual ActionResult Create(CreateFacturaConvenioPOSTViewModel createFacturaVM)
        {
            if (ModelState.IsValid)
            {
                string RazonSocial = createFacturaVM.razon ? "Comercializadora Tecnosop" : "Juan Carlos Maldonado Huerta";

                FACTURA_CONVENIO factura = new FACTURA_CONVENIO
                {
                    NUMERO_CONVENIO = createFacturaVM.NumeroConvenio,
                    NUMERO_FACTURA = createFacturaVM.id,
                    RAZON_SOCIAL = RazonSocial
                };

                db.FACTURA_CONVENIO.Add(factura);
                db.SaveChanges();

                FACTURA F = db.FACTURA.Find(factura.NUMERO_FACTURA, RazonSocial);

                F.COND_DE_VENTA = createFacturaVM.Factura.COND_DE_VENTA;
                F.FECHA_INSTALACION = createFacturaVM.Factura.FECHA_INSTALACION;

                db.Entry(F).State = System.Data.EntityState.Modified;
                db.SaveChanges();

                return RedirectToAction(Actions.Details(factura.NUMERO_FACTURA, factura.RAZON_SOCIAL));
            }
            else
            {
                return View(Actions.Create(createFacturaVM.id, createFacturaVM.razon, createFacturaVM.RutCliente));
            }
        }

        [Authorize]
        public virtual ActionResult Edit(int id, string RazonSocial)
        {
            return View(db.FACTURA_CONVENIO.Find(id, RazonSocial));
        }

        [HttpPost]
        [Authorize]
        public virtual ActionResult Edit(EditFacturaConvenioViewModel facturaVM)
        {
            if (ModelState.IsValid)
            {
                FACTURA factura = db.FACTURA.Find(facturaVM.id, facturaVM.RazonSocial);

                factura.FECHA_INSTALACION = facturaVM.Factura.FECHA_INSTALACION;
                factura.COND_DE_VENTA = facturaVM.Factura.COND_DE_VENTA;

                db.Entry(factura).State = System.Data.EntityState.Modified;
                db.SaveChanges();

                this.ShowMessage(MessageType.Success, "Se guardaron los cambios correctamente.");
                return RedirectToAction(Actions.Details(factura.NUMERO_FACTURA, factura.RAZON_SOCIAL));
            }
            else
            {
                this.ShowMessage(MessageType.Error, "Ocurrió un error al guardar los cambios.");
                return View();
            }
            //try
            //{
            //    // TODO: Add update logic her 
 
            //    return RedirectToAction("Index");
            //}
            //catch
            //{
            //    return View();
            //}
        }

        [HttpPost]
        [Authorize]
        public virtual ActionResult Delete(int id, string RazonSocial)
        {
            try
            {
                FACTURA factura = db.FACTURA.Find(id, RazonSocial);

                db.FACTURA_CONVENIO.Remove(factura.FACTURA_CONVENIO);
                db.SaveChanges();

                db.FACTURA.Remove(factura);
                db.SaveChanges();

                this.ShowMessage(MessageType.Success, "Se eliminó la factura N°: " + id + " de la base de datos.");
            }
            catch
            {
                this.ShowMessage(MessageType.Error, "No se pudo eliminar la factura N°: " + id + " de la base de datos.");
            }

            return RedirectToAction(MVC.Facturas.Index());
        }

        #endregion

        #region "Facturación de mediciones y cargos fijos"

        [Authorize]
        public virtual ActionResult DetalleFactura(int id, string RazonSocial, int? ID_MEDICION)
        {
            FACTURA_CONVENIO factura = db.FACTURA_CONVENIO.Find(id, RazonSocial);
            IEnumerable<EQUIPO_EN_CONVENIO> EquiposEnConvenio = db.CONVENIO.Find(factura.NUMERO_CONVENIO).EQUIPO_EN_CONVENIO;
            List<AgregarCargoFijoViewModel> CargosFijos = new List<AgregarCargoFijoViewModel>();

            foreach (var equipo in EquiposEnConvenio)
            {
                // Si el equipo ya esta en el detalle del cargo fijo => estaba agregado a la factura, no lo muestro de nuevo
                if (equipo.DETALLE_CARGO_FIJP.Any(detalleCargoFijo => detalleCargoFijo.NUMERO_FACTURA == factura.NUMERO_FACTURA))
                {
                    continue;
                }

                AgregarCargoFijoViewModel cargoFijo = new AgregarCargoFijoViewModel
                {
                    ID_EQUIPO_EN_CONVENIO = equipo.NUMERO_EQUIPO_EN_CONVENIO,
                    MARCA_EQUIPO = equipo.CONSIGNACION_EQUIPO.EQUIPO.ITEM.MARCA,
                    TIPO_EQUIPO = equipo.CONSIGNACION_EQUIPO.EQUIPO.TIPO_EQUIPO,
                    MODELO_EQUIPO = equipo.CONSIGNACION_EQUIPO.EQUIPO.ITEM.MODELO,
                    CONSIGNACION = equipo.CONSIGNACION_EQUIPO.CONSIGNACION
                };

                if (equipo.FORMA__DE_PAGO.Any(m => m.PAGO_TIPO_CARGO_FIJO != null))
                {
                    PAGO_TIPO_CARGO_FIJO TipoPagoCargoFijo = equipo.FORMA__DE_PAGO.First(m => m.PAGO_TIPO_CARGO_FIJO != null).PAGO_TIPO_CARGO_FIJO;
                    cargoFijo.MONTO_CARGO_FIJO = (int)TipoPagoCargoFijo.PRECIO_CARGO_FIJO;
                }
                else
	            {
                    cargoFijo.MONTO_CARGO_FIJO = null;
	            }

                CargosFijos.Add(cargoFijo);
            }


            MedicionViewModel medicionVM;
            if (ID_MEDICION.HasValue)
            {
                ViewBag.IDMedicion = ID_MEDICION;
                MEDICIONES medicion = db.MEDICIONES.Find(ID_MEDICION.Value);

                decimal precio_por_hoja = -1;
                var formas_de_pago = db.FORMA__DE_PAGO.Where(f => f.NUMERO_EQUIPO_EN_CONVENIO == medicion.NUMERO_EQUIPO_EN_CONVENIO);

                if (!formas_de_pago.Any())
                {
                    this.ShowMessage(MessageType.Error, "El equipo en convenio asociado a la medición no tiene ninguna forma de pago.");
                    return RedirectToAction(MVC.Convenio.DetailsEquipoEnConvenio(factura.NUMERO_CONVENIO, medicion.NUMERO_EQUIPO_EN_CONVENIO));
                }
                if (formas_de_pago.Count() == 1 && (formas_de_pago.First().ES_CARGO_FIJO ?? false))
                {
                    this.ShowMessage(MessageType.Error, "No existen mínimos garantizados para el equipo, ingrese alguno.");
                    return RedirectToAction(MVC.Convenio.DetailsEquipoEnConvenio(factura.NUMERO_CONVENIO, medicion.NUMERO_EQUIPO_EN_CONVENIO));
                }

                if (medicion.MEDICION__BN)
                {
                    MING_B_N min = formas_de_pago.First(f => f.MING_B_N != null).MING_B_N;
                    if (min != null && min.PRECIO_HOJA.HasValue)
                    {
                        precio_por_hoja = min.PRECIO_HOJA.Value;
                    }
                }
                else
                {
                    MING_COLOR min = formas_de_pago.FirstOrDefault(f => f.MING_COLOR != null).MING_COLOR;
                    if (min != null && min.PRECIO_HOJA.HasValue)
                    {
                        precio_por_hoja = min.PRECIO_HOJA.Value;
                    }
                }

                int? cant_facturada = medicion.DETALLE_MEDICION.Where(f => !f.DETALLE_FACTURA_CONVENIO1.FACTURA_CONVENIO.FACTURA.NULA).Sum(m => m.DIFERENCIA_FACTURADA);
                medicionVM = new MedicionViewModel
                {
                    NUMERO_FACTURA = factura.NUMERO_FACTURA,
                    RAZON_SOCIAL = factura.RAZON_SOCIAL,

                    ID_MEDICION = ID_MEDICION.Value,
                    FECHA_MEDICION = medicion.FECHA_MEDICION_,
                    DIFERENCIA = medicion.DIFERENCIA_ ?? 0,
                    CONTADOR_ANTERIOR = medicion.CONTADOR_ANTERIOR_ ?? 0,
                    CANTIDAD_FACTURADA = cant_facturada ?? 0,
                    CANTIDAD_NO_FACTURADA = (medicion.DIFERENCIA_ ?? 0) - (cant_facturada ?? 0),
                    CANTIDAD_A_FACTURAR = medicion.CONTADOR_ACTUAL_,
                    MEDICION_BN = medicion.MEDICION__BN,
                    PRECIO_POR_HOJA = precio_por_hoja
                };
            }
            else
            {
                medicionVM = new MedicionViewModel
                {
                    ID_MEDICION = -1
                };
            }
            //medicionVM.ID_MEDICION != -1 ? 

            return View(new ModificacionDetalleFacturaConvenioViewModel
                {
                    NUMERO_FACTURA = factura.NUMERO_FACTURA,
                    RAZON_SOCIAL = factura.RAZON_SOCIAL,
                    Factura = factura,
                    NUMERO_CONVENIO = factura.NUMERO_CONVENIO,
                    Convenio = factura.CONVENIO,
                    EquiposCargosFijos = CargosFijos,
                    MedicionesNoFacturadas = db.MEDICIONES
                        .Where(m => m.EQUIPO_EN_CONVENIO.CONVENIO.NUMERO_CONVENIO == factura.CONVENIO.NUMERO_CONVENIO
                        && !m.MEDICION_FACTURADA).ToList(),
                    DetalleMedicion = medicionVM
                });
        }

        [HttpPost]
        [Authorize]
        public virtual ActionResult AgregarMedicionDetalleFactura([Bind(Prefix = "DetalleMedicion")]MedicionViewModel medicionVM)
        {
            try
            {
                /* ------------ DATOS ------------ */
                MEDICIONES medicion = db.MEDICIONES.Find(medicionVM.ID_MEDICION);
                FACTURA_CONVENIO factura = db.FACTURA_CONVENIO.Find(medicionVM.NUMERO_FACTURA, medicionVM.RAZON_SOCIAL);

                int total_facturado = medicion.DETALLE_MEDICION.Where(f => !f.DETALLE_FACTURA_CONVENIO1.FACTURA_CONVENIO.FACTURA.NULA).Sum(m => m.DIFERENCIA_FACTURADA) + medicionVM.CANTIDAD_A_FACTURAR;
                bool medicion_facturada = false;
                /* ------------------------------- */

                /* --------- VALIDACIONES --------- */
                if (medicion.DETALLE_MEDICION.Count() > 0)
                {
                    if (total_facturado > (medicion.DIFERENCIA_ ?? 0))
                    {
                        this.ShowMessage(MessageType.Error, "El monto final de la facturación es mayor a lo medido.");
                        return RedirectToAction(Actions.DetalleFactura(medicionVM.NUMERO_FACTURA, medicionVM.RAZON_SOCIAL, null));
                    }
                }
                /* -------------------------------- */

                /* --- Creo el detalle de la factura. Necesito el Num del detalle --- */

                /* --- Necesito el precio por hoja y el valor del detalle --- */
                decimal precio_total = 0;
                decimal precio_por_hoja = 0;
                int cantidad_min_garantizado = 0;
                var formas_de_pago = db.FORMA__DE_PAGO.Where(f => f.NUMERO_EQUIPO_EN_CONVENIO == medicion.NUMERO_EQUIPO_EN_CONVENIO);

                if (medicion.MEDICION__BN)
                {
                    if (formas_de_pago.Count(f => f.MING_B_N != null) != 1) /* Si no hay (:= 0) formas de pago, o hay mas de una (:= > 1; -1 <) */
                    {
                        this.ShowMessage(MessageType.Error, "No se puede determinar el precio por hoja en blanco y negro.");
                        return RedirectToAction(Actions.DetalleFactura(medicionVM.NUMERO_FACTURA, medicionVM.RAZON_SOCIAL, null));
                    }

                    MING_B_N min = formas_de_pago.First(f => f.MING_B_N != null).MING_B_N;

                    if (min.FORMA__DE_PAGO.UF)
                    {
                        int valorUF = 0;
                        try
                        {
                            valorUF = db.PARAMETROS.Single(p => (p.ULTIMA_MODIFICACION_UF.CompareTo(DateTime.Today) == 0)).VALOR_UF;
                        }
                        catch (Exception)
                        {
                            this.ShowMessage(MessageType.Warning, "Debe actualizar el valor de la UF antes de agregar el detalle");
                            return RedirectToAction(MVC.Facturas.ActualizarUF());
                        }
                        precio_por_hoja = valorUF * (min.FORMA__DE_PAGO.FACTOR_UF ?? 0);
                        precio_total = medicionVM.CANTIDAD_A_FACTURAR * precio_por_hoja;
                    }
                    else
                    {
                        if (min != null && min.PRECIO_HOJA.HasValue)
                        {
                            precio_por_hoja = min.PRECIO_HOJA.Value;
                            precio_total = medicionVM.CANTIDAD_A_FACTURAR * min.PRECIO_HOJA.Value;
                        }
                        else
                        {
                            this.ShowMessage(MessageType.Error, "El precio por hoja en blanco y negro no está definido.");
                            return RedirectToAction(Actions.DetalleFactura(medicionVM.NUMERO_FACTURA, medicionVM.RAZON_SOCIAL, null));
                        }
                    }

                    cantidad_min_garantizado = min.MING ?? 0;
                }
                else
	            {
                    if (formas_de_pago.Count(f => f.MING_COLOR != null) != 1)
                    {
                        this.ShowMessage(MessageType.Error, "No se puede determinar el precio por hoja en blanco y negro.");
                        return RedirectToAction(Actions.DetalleFactura(medicionVM.NUMERO_FACTURA, medicionVM.RAZON_SOCIAL, null));
                    }

		            MING_COLOR min = formas_de_pago.FirstOrDefault(f => f.MING_COLOR != null).MING_COLOR;

                    if (min.FORMA__DE_PAGO.UF)
                    {
                        int valorUF = 0;
                        try
                        {
                            valorUF = db.PARAMETROS.Single(p => (p.ULTIMA_MODIFICACION_UF.CompareTo(DateTime.Today) == 0)).VALOR_UF;
                        }
                        catch (Exception)
                        {
                            this.ShowMessage(MessageType.Warning, "Debe actualizar el valor de la UF antes de agregar el detalle");
                            return RedirectToAction(MVC.Facturas.ActualizarUF());
                        }
                        precio_por_hoja = valorUF * (min.FORMA__DE_PAGO.FACTOR_UF ?? 0);
                        precio_total = medicionVM.CANTIDAD_A_FACTURAR * precio_por_hoja;
                        

                        if (!min.FORMA__DE_PAGO.FACTOR_UF.HasValue)
                        {
                            this.ShowMessage(MessageType.Warning, "El factor UF asociado al equipo en convenio es 0. Debería cambiarlo");
                        }
                    }
                    else
                    {
                        if (min != null && min.PRECIO_HOJA.HasValue)
                        {
                            precio_por_hoja = min.PRECIO_HOJA.Value;
                            precio_total = medicionVM.CANTIDAD_A_FACTURAR * min.PRECIO_HOJA.Value;
                        }
                        else
                        {
                            this.ShowMessage(MessageType.Error, "El precio por hoja en color no está definido.");
                            return RedirectToAction(Actions.DetalleFactura(medicionVM.NUMERO_FACTURA, medicionVM.RAZON_SOCIAL, null));
                        }
                    }

                    cantidad_min_garantizado = min.MING ?? 0;
                }

                /* ---------------------------------------------------------- */

                DETALLE_FACTURA_CONVENIO detalle_factura = new DETALLE_FACTURA_CONVENIO
                {
                    SUBTOTAL_ = (int)precio_total
                };

                factura.DETALLE_FACTURA_CONVENIO.Add(detalle_factura);
                factura.FACTURA.TOTAL += (int)precio_total;
                if (medicion.DIFERENCIA_ < cantidad_min_garantizado)
                {
                    factura.FACTURA.TOTAL += (int)((cantidad_min_garantizado - medicion.DIFERENCIA_) * precio_por_hoja);
                }
                db.SaveChanges();
                /* ------------------------------------------------------------------ */

                //YA NO HAY MEDICION_FACTURADA
                /* --- Creo la medicion que facturo. Necesito el Num de la medicion facturada --- */
                //MEDICION_FACTURADA medicion_a_facturar = new MEDICION_FACTURADA
                //{
                //    //DETALLE_FACTURA_CONVENIO = detalle_factura.DETALLE_FACTURA_CONVENIO1,
                //    ID_MEDICION = medicionVM.ID_MEDICION,
                //    NUMERO_FACTURA = factura.NUMERO_FACTURA,
                //    RAZON_SOCIAL = factura.RAZON_SOCIAL,
                //    DIFERENCIA_FACTURADA = medicionVM.CANTIDAD_A_FACTURAR,

                //    //DETALLE_FACTURA_CONVENIO = medicion.
                //};

                //db.MEDICION_FACTURADA.Add(medicion_a_facturar);
                //db.SaveChanges();
                /* ------------------------------------------------------------------------------ */

                /* --- Creo el detalle de la medicion. Lo asocio al detalle de la factura y a su medicion facturada --- */
                DETALLE_MEDICION detalle = new DETALLE_MEDICION
                {
                    DETALLE_FACTURA_CONVENIO = detalle_factura.DETALLE_FACTURA_CONVENIO1,
                    //COD_DIFERENCIA_FACTURADA = medicion_a_facturar.COD_DIFERENCIA_FACTURADA,
                    NUMERO_FACTURA = factura.NUMERO_FACTURA,
                    RAZON_SOCIAL = factura.RAZON_SOCIAL,
                    BN = medicion.MEDICION__BN,
                    FECHA_ADICION = DateTime.Today,
                    DIFERENCIA_FACTURADA = medicionVM.CANTIDAD_A_FACTURAR,
                    //SUBTOTAL_MIN = 
                };

                medicion.DETALLE_MEDICION.Add(detalle);
                db.SaveChanges();
                /* ---------------------------------------------------------------------------------------------------- */

                /* --- Guardo el estado de la medicion; facturada o no --- */
                medicion_facturada = medicionVM.MEDICION_FACTURADA ? true : total_facturado == medicion.DIFERENCIA_;

                medicion.MEDICION_FACTURADA = medicion_facturada;// ?? false;
                db.Entry(medicion).State = System.Data.EntityState.Modified;
                db.SaveChanges();
                /* ------------------------------------------------------- */

                this.ShowMessage(MessageType.Success, "Se agregó correctamente el detalle a la factura.");
            }
            catch
            {
                this.ShowMessage(MessageType.Error, "Ocurrió un error al intentar agregar el detalle a la factura.");
            }

            return RedirectToAction(Actions.DetalleFactura(medicionVM.NUMERO_FACTURA, medicionVM.RAZON_SOCIAL, null));
        }

        [HttpPost]
        [Authorize]
        public virtual ActionResult AgregarCargoFijoDetalleFactura(int NUMERO_FACTURA, string RAZON_SOCIAL, int? Equipo_En_Convenio)
        {
            if (!Equipo_En_Convenio.HasValue)
            {
                this.ShowMessage(MessageType.Error, "Elija un equipo en convenio del cual se agrege el cargo fijo.");
            }
            else
            {
                try
                {
                    FACTURA_CONVENIO factura = db.FACTURA_CONVENIO.Find(NUMERO_FACTURA, RAZON_SOCIAL);

                    DETALLE_FACTURA_CONVENIO detalle = new DETALLE_FACTURA_CONVENIO
                    {
                        SUBTOTAL_ = 0
                    };

                    factura.DETALLE_FACTURA_CONVENIO.Add(detalle);
                    db.SaveChanges();

                    EQUIPO_EN_CONVENIO equipoEnConvenio = db.EQUIPO_EN_CONVENIO.Find(factura.NUMERO_CONVENIO, Equipo_En_Convenio.Value);
                    PAGO_TIPO_CARGO_FIJO cargoFijo = equipoEnConvenio.FORMA__DE_PAGO.First(f => f.PAGO_TIPO_CARGO_FIJO != null).PAGO_TIPO_CARGO_FIJO;
                    int valorUF = db.PARAMETROS.Single(p => p.ULTIMA_MODIFICACION_UF.CompareTo(DateTime.Today) == 0).VALOR_UF;

                    DETALLE_CARGO_FIJP detalleCargoFijo = new DETALLE_CARGO_FIJP
                    {
                        DETALLE_FACTURA_CONVENIO1 = detalle,
                        EQUIPO_EN_CONVENIO = equipoEnConvenio,
                        SUBTOTAL_ = cargoFijo.FORMA__DE_PAGO.UF ? (int)(valorUF * cargoFijo.PRECIO_CARGO_FIJO) : (int)cargoFijo.PRECIO_CARGO_FIJO
                    };

                    detalle.DETALLE_CARGO_FIJP = detalleCargoFijo;
                    detalle.SUBTOTAL_ = detalleCargoFijo.SUBTOTAL_;
                    factura.FACTURA.TOTAL += detalle.SUBTOTAL_;

                    db.SaveChanges();
                    this.ShowMessage(MessageType.Success, "Se agregó el cargo dijo al detalle de la factura.");
                }
                catch (Exception)
                {
                    this.ShowMessage(MessageType.Error, "No se pudo agregar el cargo fijo al detalle de la factura.");
                }
            }

            return RedirectToAction(Actions.DetalleFactura(NUMERO_FACTURA, RAZON_SOCIAL, null));
        }

        [HttpPost]
        [Authorize]
        public virtual ActionResult QuitarLineaDetalleFactura(int NUMERO_FACTURA, string RAZON_SOCIAL)
        {
            int NUMERO_LINEA_DETALLE;
            if (Int32.TryParse(Request.QueryString["NUMERO_LINEA_DETALLE"], out NUMERO_LINEA_DETALLE))
            {
                try
                {
                    FACTURA_CONVENIO factura = db.FACTURA_CONVENIO.Find(NUMERO_FACTURA, RAZON_SOCIAL);
                    DETALLE_FACTURA_CONVENIO detalle = factura.DETALLE_FACTURA_CONVENIO.Single(df => df.DETALLE_FACTURA_CONVENIO1 == NUMERO_LINEA_DETALLE);
                    int resto = detalle.SUBTOTAL_.Value;

                    if (detalle.DETALLE_CARGO_FIJP != null)
                    {
                        DETALLE_CARGO_FIJP cargoFijo = detalle.DETALLE_CARGO_FIJP;
                        db.DETALLE_CARGO_FIJP.Remove(cargoFijo);
                    }
                    else if (detalle.DETALLE_MEDICION != null)
                    {
                        DETALLE_MEDICION detalleMedicion = detalle.DETALLE_MEDICION;
                        detalleMedicion.MEDICIONES.MEDICION_FACTURADA = false;

                        int? minimo_garantizado = GetMinGarantizado(detalleMedicion.MEDICIONES.NUMERO_CONVENIO, detalleMedicion.MEDICIONES.NUMERO_EQUIPO_EN_CONVENIO, detalleMedicion.MEDICIONES.MEDICION__BN);
                        if (minimo_garantizado.HasValue)
                        {
                            if (detalleMedicion.MEDICIONES.DIFERENCIA_ < minimo_garantizado)
                            {
                                decimal? precio_por_hoja = GetPrecioPorHoja(detalleMedicion.MEDICIONES.NUMERO_CONVENIO, detalleMedicion.MEDICIONES.NUMERO_EQUIPO_EN_CONVENIO, detalleMedicion.MEDICIONES.MEDICION__BN, detalleMedicion.FECHA_ADICION);
                                factura.FACTURA.TOTAL -= (int)((minimo_garantizado - detalleMedicion.MEDICIONES.DIFERENCIA_) * precio_por_hoja ?? 0);
                            }
                        }

                        db.DETALLE_MEDICION.Remove(detalleMedicion);
                    }

                    db.SaveChanges();

                    db.DETALLE_FACTURA_CONVENIO.Remove(detalle);
                    factura.FACTURA.TOTAL -= resto;

                    db.SaveChanges();

                    this.ShowMessage(MessageType.Success, "Se eliminó la linea de detalle.");
                }
                catch (Exception)
                {
                    this.ShowMessage(MessageType.Error, "Ocurrió un error al intentar eliminar la linea de detalle.");
                }
            }
            else
            {
                this.ShowMessage(MessageType.Error, "No se pudo eliminar la linea de detalle.");
            }

            return RedirectToAction(Actions.Details(NUMERO_FACTURA, RAZON_SOCIAL));
        }

        // Asumo que el minimo garantizado correspondiente existe
        private int? GetMinGarantizado(int ID_CONVENIO, int ID_EQUIPO_EN_CONVENIO, bool en_BN)
        {
            EQUIPO_EN_CONVENIO equipo = db.EQUIPO_EN_CONVENIO.Find(ID_CONVENIO, ID_EQUIPO_EN_CONVENIO);

            return en_BN ? equipo.FORMA__DE_PAGO.First(f => f.MING_B_N != null).MING_B_N.MING : equipo.FORMA__DE_PAGO.First(f => f.MING_COLOR != null).MING_COLOR.MING;
        }

        // Asumo que el minimo garantizado correspondiente existe
        private decimal? GetPrecioPorHoja(int ID_CONVENIO, int ID_EQUIPO_EN_CONVENIO, bool en_BN, DateTime fecha_detalle)
        {
            EQUIPO_EN_CONVENIO equipo = db.EQUIPO_EN_CONVENIO.Find(ID_CONVENIO, ID_EQUIPO_EN_CONVENIO);
            int valorUF = db.PARAMETROS.Single(p => p.ULTIMA_MODIFICACION_UF.CompareTo(fecha_detalle) == 0).VALOR_UF;

            if (en_BN)
            {
                MING_B_N minBN = equipo.FORMA__DE_PAGO.First(f => f.MING_B_N != null).MING_B_N;//.PRECIO_HOJA;

                return minBN.FORMA__DE_PAGO.UF ? minBN.FORMA__DE_PAGO.FACTOR_UF * valorUF : minBN.PRECIO_HOJA;
            }
            else
            {
                MING_COLOR minC = equipo.FORMA__DE_PAGO.First(f => f.MING_COLOR != null).MING_COLOR;

                return minC.FORMA__DE_PAGO.UF ? minC.FORMA__DE_PAGO.FACTOR_UF * valorUF : minC.PRECIO_HOJA;
            }
        }

        #endregion

        [Authorize]
        public virtual ActionResult CambiarDescuento(int NUMERO_FACTURA, string RAZON_SOCIAL)
        {
            int DESCUENTO = Convert.ToInt32(Request.Form["FACTURA.DESCUENTO"]);
            FACTURA factura = db.FACTURA.Find(NUMERO_FACTURA, RAZON_SOCIAL);

            factura.DESCUENTO = DESCUENTO;
            db.Entry(factura).State = System.Data.EntityState.Modified;
            db.SaveChanges();

            this.ShowMessage(MessageType.Success, "El descuento fue aplicado para esta factura.");

            return RedirectToAction(Actions.Details(NUMERO_FACTURA, RAZON_SOCIAL));
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}
